package com.googlecode.kipler.syntax.concept;

import com.googlecode.kipler.common.Copyable;

/**
 * This class represents a concept expression formed by a binary constructor of
 * union or intersection from description logics.
 * 
 * @author İnanç Seylan
 * 
 */
public class BooleanConcept extends Concept {

	/**
	 * The binary constructor used in this concept expression
	 */
	private BooleanConstructor constructor;

	/**
	 * The concept that is on the left-hand side of this binary concept
	 * expression
	 */
	private Concept lhs;

	/**
	 * The concept that is on the right-hand side of this binary concept
	 * expression
	 */
	private Concept rhs;

	/**
	 * Default constructor
	 */
	public BooleanConcept() {

	}

	/**
	 * Constructor to initialize all the fields of this class by the given ones.
	 * 
	 * @param constructor
	 *            The type of the binary constructor in the concept expression
	 * @param lhs
	 *            The concept that is on the left-hand side of the binary
	 *            constructor
	 * @param rhs
	 *            The concept that is on the right-hand side of the binary
	 *            constructor
	 */
	public BooleanConcept(BooleanConstructor constructor, Concept lhs, Concept rhs) {
		setConstructor(constructor);
		setLhs(lhs);
		setRhs(rhs);
	}

	/**
	 * @return the binary constructor used in this concept expression.
	 */
	public BooleanConstructor getConstructor() {
		return constructor;
	}

	/**
	 * Sets the binary constructor used in this concept expression.
	 * 
	 * @param constructor
	 *            The binary constructor
	 */
	public void setConstructor(BooleanConstructor constructor) {
		this.constructor = constructor;
	}

	/**
	 * @return the left-hand side concept of this binary concept expression.
	 */
	public Concept getLhs() {
		return lhs;
	}

	/**
	 * Sets the left-hand side concept of this binary concept expression.
	 * 
	 * @param lhs
	 *            The left-hand side concept
	 */
	public void setLhs(Concept lhs) {
		this.lhs = lhs;
	}

	/**
	 * @return the right-hand side concept of this binary concept expression.
	 */
	public Concept getRhs() {
		return rhs;
	}

	/**
	 * Sets the right-hand side concept of this binary concept expression.
	 * 
	 * @param rhs
	 *            The right-hand side concept
	 */
	public void setRhs(Concept rhs) {
		this.rhs = rhs;
	}

	/**
	 * @return false
	 */
	public boolean isNegated() {
		return false;
	}

	/**
	 * Changes the expression to union if it's an intersection or vice versa.
	 */
	public Concept toggleNegated() {
		if (getConstructor() == BooleanConstructor.INTERSECTION) {
			setConstructor(BooleanConstructor.UNION);
		} else {
			setConstructor(BooleanConstructor.INTERSECTION);
		}
		setLhs(getLhs().toggleNegated());
		setRhs(getRhs().toggleNegated());
		return this;
	}

	/**
	 * @return true if both concepts have the same constructor and operands.
	 *         Operand order does not matter in the equality check.
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj != null && (obj.getClass().equals(this.getClass()))) {
			BooleanConcept other = (BooleanConcept) obj;
			boolean matchedOpsEqual = this.getLhs().equals(other.getLhs())
					&& this.getRhs().equals(other.getRhs());
			boolean unmatchedOpsEqual = this.getLhs().equals(other.getRhs())
					&& this.getRhs().equals(other.getLhs());
			return (this.isLeftBiased() == other.isLeftBiased()
					&& this.getConstructor() == other.getConstructor() && (matchedOpsEqual || unmatchedOpsEqual));
		}
		return false;
	}

	/**
	 * @see tr.edu.ege.seagent.alcbi.syntax.description.Concept#hashCode()
	 */
	@Override
	public int hashCode() {
		return 17 * 37 + new Boolean(isLeftBiased()).hashCode()
				+ getConstructor().hashCode() + getLhs().hashCode()
				+ getRhs().hashCode();
	}

	/**
	 * Calls {@link ConceptVisitor#visitBooleanConcept(BooleanConcept)} on
	 * itself.
	 */
	public void accept(ConceptVisitor v) {
		v.visitBooleanConcept(this);
	}

	/**
	 * @see Copyable#copy()
	 */
	public BooleanConcept copy() {
		BooleanConcept bd = new BooleanConcept();
		bd.setConstructor(getConstructor());
		bd.setLhs(getLhs().copy());
		bd.setRhs(getRhs().copy());
		bd.setLeftBiased(isLeftBiased());
		return bd;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("(");
		buffer.append(getLhs());
		if (getConstructor() == BooleanConstructor.INTERSECTION)
			buffer.append(" & ");
		else
			buffer.append(" | ");
		buffer.append(getRhs());
		buffer.append(")");
		return buffer.toString();
	}

	@Override
	public void setLeftBiased(boolean value) {
		super.setLeftBiased(value);
		getLhs().setLeftBiased(value);
		getRhs().setLeftBiased(value);
	}

}
